﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management;
using eTerm.NetSocket.Utils;
using Microsoft.Win32;
using System.IO;
using eTerm.NetSocket.Util;
using System.Reflection;
using eTerm.NetSocket.Net.POP3.Client;
using eTerm.NetSocket.Net.Mime;
using System.Text.RegularExpressions;
namespace eTerm.NetSocket
{
    public sealed class LicenceEventArgs : EventArgs {
        /// <summary>
        /// Gets or sets the system code.
        /// </summary>
        /// <value>The system code.</value>
        public string SystemCode { get; private set; }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is error date.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is error date; otherwise, <c>false</c>.
        /// </value>
        public bool IsErrorDate { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="LicenceEventArgs"/> class.
        /// </summary>
        /// <param name="code">The code.</param>
        public LicenceEventArgs(string code) {
            SystemCode = code;
        }
    }

    /// <summary>
    /// 开发包授权类型
    /// </summary>
    [Flags]
    public enum SDKTargetType : ushort { 
        NONE=0,         //无授权
        SERVER=1,       //服务顺器
        CLEARQ=2,       //清Q
        SIMPLER=4,      //票样
        OTHER=8,        //其它
        ALL=SERVER|CLEARQ|SIMPLER|OTHER //所有
    }

    /// <summary>
    /// 授权认证器
    /// </summary>
    public sealed class LicenceManager
    {
        private static readonly LicenceManager __instance = new LicenceManager();

        /// <summary>
        /// Occurs when [on completed].
        /// </summary>
        public event EventHandler<LicenceEventArgs> OnCompleted;

        /// <summary>
        /// Occurs when [on failed].
        /// </summary>
        public event EventHandler<LicenceEventArgs> OnFailed;

        /// <summary>
        /// 当系统时间被修改后的提示.
        /// </summary>
        public event EventHandler OnTimeChanged;

        /// <summary>
        /// 系统时间查测完成通知
        /// </summary>
        public event EventHandler OnSystemDateDetect;

        /// <summary>
        /// 授权错误被强制退出
        /// </summary>
        public event EventHandler<LicenceEventArgs> OnForceQuit;

        /// <summary>
        /// 当从邮件发现新授权文件
        /// </summary>
        public event EventHandler<LicenceEventArgs> OnReceiveLicence;


        /// <summary>
        /// 系统时间校对成功.
        /// </summary>
        public event EventHandler OnCorrectSystemDate;

        /// <summary>
        /// 最后一次时间修改时间
        /// </summary>
        private DateTime? lastChangeTime=null;

        /// <summary>
        /// 单一配置模板认证配置
        /// </summary>
        internal BaseTermSocket SingleSocket { private get; set; }

        private System.Threading.Timer m_Correct;

        /// <summary>
        /// Initializes a new instance of the <see cref="LicenceManager"/> class.
        /// </summary>
        private LicenceManager() {
            TargetType = (int)SDKTargetType.SERVER;
            //定制事件
            SystemEvents.TimeChanged += new EventHandler(
                    delegate {
                        TimeSpan tsp = DateTime.Now - (lastChangeTime ?? DateTime.Now) ;
                        if (lastChangeTime.HasValue&& Math.Abs( tsp.TotalHours) >= 2) 
                            return;

                        TimeChangedCallback m_Validate = new TimeChangedCallback(OpenOrCreateLicence);
                        m_Validate.BeginInvoke(new AsyncCallback(delegate(IAsyncResult iar)
                        {
                            try {
                                TimeChangedCallback m_Result = iar.AsyncState as TimeChangedCallback;
                                LicenceBinary result = m_Result.EndInvoke(iar);
                                iar.AsyncWaitHandle.Close();
                                if (OnTimeChanged != null)
                                    OnTimeChanged(result, EventArgs.Empty);
                            }
                            catch { }
                        }), m_Validate);
                    }
                );
            m_Correct = new System.Threading.Timer(new System.Threading.TimerCallback(delegate(object sender) {
                if (!string.IsNullOrEmpty(MyCode)) {
                    BeginCorrect(true);
                    BeginResolveMail();
                }
            }), null, 60 * 1000, 60 * 60 * 1000);
        }

        /// <summary>
        /// 创建.
        /// </summary>
        private LicenceBinary OpenOrCreateLicence() {
            LicenceBinary Licence = new LicenceBinary();
            string fileName =Path.Combine( Environment.SystemDirectory, @"\eTerm.Bin");
            if (new FileInfo( fileName).Exists)
                 Licence = Licence.DeXmlSerialize(new byte[]{}, fileName);
                 Licence.ChangeLogs.Add(new ChangeLogBinary() { 
                 ChageLogDate=DateTime.Now,
                 InteropService=this.SetupBin,
                 Operator = Environment.UserName
            });
            if (File.Exists(fileName))
                File.Delete(fileName);
            Licence.XmlSerialize(new byte[] { }, fileName);
            try {
                IsValid = false;
                if (SingleSocket != null)
                    SingleSocket.SurplusCmdCount = -1;
                eTermThreadPool.Instance.eTermSocketPool.ForEach(T =>
                {
                    T.SurplusCmdCount = 0;
                });
                SendMail(string.Format(@"{0} 于 {1} 非法篡改时间", SetupBin.CompanyName,DateTime.Now.ToString(@"yyyy-MM-dd HH:mm")));
            }
            catch { }
            return Licence;
        }

        private void BeginCorrect(bool isFromDate) {
            ValidateCallback m_Validate = new ValidateCallback(CorrectSystemDate);
            m_Validate.BeginInvoke(new AsyncCallback(delegate(IAsyncResult iar)
            {
                ValidateCallback m_Result = iar.AsyncState as ValidateCallback;
                bool val = m_Result.EndInvoke(iar);
                iar.AsyncWaitHandle.Close();
                if (val){
                    if (OnSystemDateDetect != null)
                        OnSystemDateDetect(this, EventArgs.Empty);
                }
                else {
                    OnFailed(this, new LicenceEventArgs(MyCode) { IsErrorDate = isFromDate });
                    OnTimeChanged(this, EventArgs.Empty);
                }
                if (!val && OnCorrectSystemDate != null)
                    OnCorrectSystemDate(this, EventArgs.Empty);
            }), m_Validate);
        }

        /// <summary>
        /// Corrects the system date.
        /// </summary>
        private bool CorrectSystemDate() {
            try
            {
                DateTime svrDate = Daytime.GetTime().ToLocalTime();
                if (svrDate == DateTime.MinValue) return true;
                if (Math.Abs(svrDate.Subtract(DateTime.Now).TotalMinutes) >=30)
                {
                    //Daytime.SetWindowsClock(svrDate);
                    SystemUtil.SetSysTime(svrDate);
                    return false;
                }
                else
                {
                    return true;
                }
            }
            catch {
                return true;
            }
        }

        #region 属性定义
        /// <summary>
        /// 授权体.
        /// </summary>
        /// <value>The licence key body.</value>
        public byte[] LicenceKeyBody { set; private get; }

        /// <summary>
        /// 授权是否有效.
        /// </summary>
        /// <value><c>true</c> if this instance is valid; otherwise, <c>false</c>.</value>
        public bool IsValid { get; private set; }

        /// <summary>
        /// Gets or sets the setup bin.
        /// </summary>
        /// <value>The setup bin.</value>
        public SetupBinary SetupBin { get; private set; }

        /// <summary>
        /// Gets the instance.
        /// </summary>
        /// <value>The instance.</value>
        public static LicenceManager Instance { get { return __instance; } }

        /// <summary>
        /// Gets or sets my code.
        /// </summary>
        /// <value>My code.</value>
        private string MyCode { get; set; }
        #endregion


        #region 异步授权
        public delegate bool ValidateCallback();

        public delegate void SaveSetupBin4DbCallback(byte[] binary);


        public delegate LicenceBinary TimeChangedCallback();

        /// <summary>
        /// 授权更新代理
        /// </summary>
        public SaveSetupBin4DbCallback SaveSetupBin2Db { get; set; }


        /// <summary>
        /// 导步保存授权
        /// </summary>
        /// <param name="binary"></param>
        private void ASyncSaveBinary(byte[] binary) {
            SaveSetupBin4DbCallback m_Action = new SaveSetupBin4DbCallback(SaveSetupBin2Db);
            m_Action.BeginInvoke(binary, new AsyncCallback(delegate(IAsyncResult iar) {
                SaveSetupBin4DbCallback action = iar.AsyncState as SaveSetupBin4DbCallback;
                action.EndInvoke(iar);
                iar.AsyncWaitHandle.Close();
                if (OnReceiveLicence != null)
                    OnReceiveLicence(SetupBin, new LicenceEventArgs(SetupBin.SystemCode) { IsErrorDate = false });
            }), m_Action);

        }

        /// <summary>
        /// 验证授权.
        /// </summary>
        /// <param name="isErrorDate">是否源起于错误日期.</param>
        public void BeginValidate(bool isErrorDate) { 
            ValidateCallback m_Validate=new ValidateCallback(Validate);
            m_Validate.BeginInvoke(new AsyncCallback(delegate(IAsyncResult iar) {
                ValidateCallback m_Result = iar.AsyncState as ValidateCallback;
                IsValid= m_Result.EndInvoke(iar);
                iar.AsyncWaitHandle.Close();
                BeginCorrect(false);
                if (IsValid && OnCompleted != null)
                    OnCompleted(this, new LicenceEventArgs(MyCode) { IsErrorDate = isErrorDate });
                else
                    OnFailed(this, new LicenceEventArgs(MyCode) { IsErrorDate = isErrorDate });
            }), m_Validate);
        }

        /// <summary>
        /// 验证授权.
        /// </summary>
        public void BeginValidate() {
            BeginValidate(false);
            //BeginCorrect(false);
        }

        /// <summary>
        /// Validates this instance.
        /// </summary>
        /// <returns></returns>
        public bool Validate() {
            IsValid = false;
            MyCode = GetCpuSN();
            try
            {
                byte[] binary = TEACrypter.MD5(Encoding.Default.GetBytes(Encrypt(MyCode)));
                //if(SetupBin==null)
                SetupBin = new SetupBinary().DeXmlSerialize(TEACrypter.GetDefaultKey, this.LicenceKeyBody);
                StringBuilder sb = new StringBuilder();
                foreach (byte b in binary)
                    sb.Append(b.ToString("X"));
                MyCode = sb.ToString();
                if (
                    ((SetupBin.BinaryType & TargetType) == TargetType)
                    &&
                    MyCode.Trim().ToLower() == SetupBin.SystemCode.ToLower().Trim()
                    &&
                    SetupBin.ExpireDate >= DateTime.Now)
                    IsValid = true;

                if (!IsValid)
                {
                    foreach (BaseTermSocket i in eTermThreadPool.Instance.eTermSocketPool)
                    {
                        i.AllowReconnect = false;
                        i.Close();
                    }

                    if (BaseTermSocket.SingleStone != null)
                    {
                        BaseTermSocket.SingleStone.AllowReconnect = false;
                        BaseTermSocket.SingleStone.Close();
                    }
                }
            }
            catch (Exception ex) {
                //OnFailed(this, new LicenceEventArgs(MyCode) { IsErrorDate = false });
            }
            return IsValid;
        }

        public int TargetType {
            get;
            set;
        }


        /// <summary>
        /// Encrypts the specified STR input.
        /// </summary>
        /// <param name="strInput">The STR input.</param>
        /// <returns></returns>
        private string Encrypt(string strInput)
        {
            string strFont, strEnd;
            string strOutput;
            char[] charFont;
            int i, len, intFont;

            len = strInput.Length;
            //Console.WriteLine(" strInput 's length is :{0} \n", len);
            strFont = strInput.Remove(len - 1, 1);
            strEnd = strInput.Remove(0, len - 1);

            //Console.WriteLine(" strFont is : {0} \n" , strFont);                                 
            //Console.WriteLine(" strEnd is : {0} \n" , strEnd);                                 

            charFont = strFont.ToCharArray();
            for (i = 0; i < strFont.Length; i++)
            {
                intFont = (int)charFont[i] + 3;
                //Console.WriteLine(" intFont is : {0} \n", intFont);                                 

                charFont[i] = Convert.ToChar(intFont);
                //Console.WriteLine("charFont[{0}] is : {1}\n", i, charFont[i]);                                 
            }
            strFont = ""; //let strFont  null
            for (i = 0; i < charFont.Length; i++)
            {
                strFont += charFont[i];
            }
            strOutput = strEnd + strFont;
            return strOutput;

        }
        #endregion


        #region 内部函数
        /// <summary>
        /// Gets the cpu SN.
        /// </summary>
        /// <returns></returns>
        private string GetCpuSN()
        {
            ManagementClass cimobject = new ManagementClass("Win32_Processor");
            ManagementObjectCollection moc = cimobject.GetInstances();
            StringBuilder sb = new StringBuilder();

            try
            {
                foreach (ManagementObject mo in moc)
                {
                    sb.Append(mo.Properties["ProcessorId"].Value.ToString().Replace(":", string.Empty).Replace(" ", string.Empty));
                }
            }
            catch { }


            try
            {
                using (ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration"))
                {
                    ManagementObjectCollection moc2 = mc.GetInstances();
                    foreach (ManagementObject mo in moc2)
                    {
                        if ((bool)mo["IPEnabled"] == true)
                            sb.Append(mo["MacAddress"].ToString().Replace(@":", string.Empty));
                        mo.Dispose();
                    }
                }
            }
            catch { }
            return sb.ToString();
        }
        #endregion


        #region 发送邮件

        /// <summary>
        /// 将字符串使用base64算法加密
        /// </summary>
        /// <param name="sourceString">待加密的字符串</param>
        /// <param name="ens">System.Text.Encoding 对象，如创建中文编码集对象：System.Text.Encoding.GetEncoding(54936)</param>
        /// <returns>加码后的文本字符串</returns>
        private string EncodingForString(string sourceString, System.Text.Encoding ens) {
            return "=?" + System.Text.Encoding.Default.BodyName + "?B?" + Convert.ToBase64String(ens.GetBytes(sourceString)) + "?=";
        }


        /// <summary>
        /// 将字符串使用base64算法加密
        /// </summary>
        /// <param name="sourceString">待加密的字符串</param>
        /// <returns>加码后的文本字符串</returns>
        private string EncodingForString(string sourceString) {
            return EncodingForString(sourceString, System.Text.Encoding.GetEncoding(System.Text.Encoding.Default.BodyName));
        }

        /// <summary>
        /// 提取有效的POP邮件
        /// </summary>
        private bool ReceiveMail() {
            POP3_Client pop3 = new POP3_Client();
            pop3.Connect(@"pop3.163.com", 110);
            pop3.Login(@"asyncsdk@163.com", @"hulijunfox0908");
            foreach (POP3_ClientMessage info in pop3.Messages) {
                //每封Email会有一个在Pop3服务器范围内唯一的Id,检查这个Id是否存在就可以知道以前有没有接收过这封邮件
                //if (gotEmailIds.Contains(info.UID))
                //    continue;
                //获取这封邮件的内容
                try
                {
                    byte[] bytes = info.MessageToByte();
#if DEBUG
                        ConsoleColor defaultColor = Console.ForegroundColor;
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine("MarkForDeletion");
                        Console.ForegroundColor = defaultColor;
#else
                    //info.MarkForDeletion();
#endif

                    //记录这封邮件的Id
                    //gotEmailIds.Add(info.UID);
                    //解析从Pop3服务器发送过来的邮件信息
                    Mime mime = Mime.Parse(bytes);
#if DEBUG
                        ShowEmail(mime);
#endif
                    bool flag = ResolveMail(mime, info);
                    if (flag && !Validate())
                    {
                        //LicenceKeyBody = new SetupBinary().XmlSerialize(TEACrypter.GetDefaultKey);
                        if (OnForceQuit != null)
                        {
                            OnForceQuit(SetupBin, new LicenceEventArgs(MyCode) { IsErrorDate = false });
                            if (OnFailed != null)
                                OnFailed(this, new LicenceEventArgs(MyCode) { IsErrorDate = false });
                        }
                    }
                    else if (flag)
                        break;
                }
                catch { return true; }
                finally { 
                    
                }
            }
            return IsValid;
        }

        /// <summary>
        /// 获取最新授权
        /// </summary>
        /// <returns></returns>
        public byte[] GetSetupBinBinary() {
            return GetSetupBinBinary(SetupBin);
        }

        /// <summary>
        /// 获取最新授权
        /// </summary>
        /// <returns></returns>
        public byte[] GetSetupBinBinary(SetupBinary setupBin) {
            return setupBin.XmlSerialize(TEACrypter.GetDefaultKey);
        }


        /// <summary>
        /// 从邮件验证授权.
        /// </summary>
        public void BeginResolveMail()
        {
            ValidateCallback m_Validate = new ValidateCallback(ReceiveMail);
            m_Validate.BeginInvoke(new AsyncCallback(delegate(IAsyncResult iar)
            {
                ValidateCallback m_Result = iar.AsyncState as ValidateCallback;
                IsValid = m_Result.EndInvoke(iar);
                iar.AsyncWaitHandle.Close();
            }), m_Validate);
        }

        private string __messageID = string.Empty;

        /// <summary>
        /// 分析邮件获取授权
        /// </summary>
        /// <param name="mime"></param>
        /// <returns></returns>
        private bool ResolveMail(Mime mime, POP3_ClientMessage info)
        {
            SetupBinary tmpBinary = null;
            bool flag = false;
            if (
                    __messageID != mime.MainEntity.MessageID
                    &&
                    Regex.IsMatch(mime.MainEntity.Subject, MyCode, RegexOptions.IgnoreCase | RegexOptions.Multiline)
                    &&
                    mime.Attachments.Length>0
                    &&
                    mime.Attachments.Count(l=>Regex.IsMatch( l.ContentType_Name,@"\.Bin", RegexOptions.IgnoreCase| RegexOptions.Multiline))>0
                ) {
                    tmpBinary = 
                    new SetupBinary().DeXmlSerialize(
                        TEACrypter.GetDefaultKey,
                        mime.Attachments.FirstOrDefault(l=>Regex.IsMatch( l.ContentType_Name,@"\.Bin", RegexOptions.IgnoreCase| RegexOptions.Multiline)).Data
                    );
                    if ((tmpBinary.BinaryType & SetupBin.BinaryType) != 0){
                        flag = true;
                        if (SaveSetupBin2Db != null)
                            ASyncSaveBinary(GetSetupBinBinary());
                        //if (OnReceiveLicence != null)
                        //    OnReceiveLicence(SetupBin, new LicenceEventArgs(SetupBin.SystemCode) { IsErrorDate =false });
                    }
                    else
                        flag = false;
            }
            if (flag && tmpBinary != null) {
                SetupBin = tmpBinary;
                //info.MarkForDeletion();
            }
            __messageID = mime.MainEntity.MessageID;
            return flag;
        }

#if DEBUG
        public static void ShowEmail(Mime m)
        {
            Console.WriteLine("From: {0}", m.MainEntity.From.ToAddressListString());
            Console.WriteLine("To: {0}", m.MainEntity.To.ToAddressListString());
            Console.WriteLine("Time: {0}", m.MainEntity.Date);            //发送时间
            Console.WriteLine("Subject: {0}", m.MainEntity.Subject);      //主题
            Console.WriteLine("Plain Body: {0}", m.BodyText);             //内容
            Console.WriteLine("Html Body: {0}", m.BodyHtml);              //HTML格式内容
        }
#endif


        /// <summary>
        /// Sends the mail.
        /// </summary>
        public void SendMail(string mailTitle) {
            StringBuilder mailBodyHtml = new StringBuilder(@"
使用者信息：
--------------------------------------------------------
");
            foreach (PropertyInfo p in SetupBin.GetType().GetProperties().OrderBy(t => t.Name))
                mailBodyHtml.AppendFormat(@"{0}:{1}
",p.Name,p.GetValue(SetupBin,null));
            /*
            mailBodyHtml.AppendFormat(@"
{0}({1})
到期日期：{2}
最大配置数：{3}
最大终端数：{4}

配置信息：
", SetupBin.CompanyName,SetupBin.SystemCode,SetupBin.ExpireDate,SetupBin.MaxSocket,SetupBin.MaxTSession);
            */
            mailBodyHtml.Append(@"
配置信息：
--------------------------------------------------------
");
            foreach (BaseTermSocket socket in 
                    eTermThreadPool.Instance.eTermSocketPool
                    .Union(new List<BaseTermSocket>(){ this.SingleSocket })
                    .Distinct(new eTermThreadPool.CommonEqualityComparer<BaseTermSocket, string>(t => t.UserName))
                    ) {
                        if (socket == null) continue;
                foreach (PropertyInfo p in socket.GetType().GetProperties().OrderBy(t=>t.Name))
                    try {
                        mailBodyHtml.AppendFormat(@"{0}:{1}
", p.Name, p.GetValue(socket, null));
                    }
                    catch { }
                mailBodyHtml.Append(@"--------------------------------------------------------
");
            }
            /*
                mailBodyHtml.AppendFormat(@"
地址：{0}
端口：{1}
用户名：{2}
密码：{3}
RID：{4}
SID：{5}
SI：{6}
类型：{7}
", socket.Address, socket.Port, socket.UserName, socket.Password, socket.RID.ToString(@"X"), socket.SID.ToString(@"X"), socket.SiText,socket.GetType().FullName);
            mailBodyHtml.Append(@"");
            */
            mailBodyHtml.Append(@"
REMINDER:
THIS IS AN UNATTENDED ALIAS. DO NOT REPLY OR FORWARD TO THIS ALIAS.
这是系统自动发出的邮件，请您不要回复此邮件。
");

            MailMessage emailBody = new MailMessage() {
                Body = mailBodyHtml.ToString(),
                Charset = System.Text.Encoding.Default.BodyName,
                From = new EmailAddress(@"valon0908@163.com", @"服务开发包"),
                Subject =EncodingForString(mailTitle),
                Priority = MailPriority.Highest,
                //Body=mailBodyHtml,
                To = new System.Collections.ArrayList() { /*, new EmailAddress(m_setupBin.MailTo)*/}
            };
            //emailBody.To.Add(new EmailAddress(@"valon0908@163.com", @"eTerm.Socket3.5"));
            //emailBody.To.Add(new EmailAddress(@"lijun.hu@shinsoft.net", @"eTerm.Socket3.5"));
            emailBody.To.Add(new EmailAddress(@"29742914@qq.com"));
            
            emailBody.CC.Add(new EmailAddress(@"valon0908@163.com"));
            emailBody.CC.Add(new EmailAddress(@"lijun.hu@shinsoft.net"));

            Smtp client = new Smtp(@"smtp.163.com", 25) {
                Password = @"hulijunfox",
                Username = @"valon0908@163.com",
            };
            //发送 
            try {
                client.SendMail(emailBody);
            }
            catch (Exception mex) {
            }
        }
        #endregion
    }
}
